<!DOCTYPE html>
<html lang="en">
<head>

    <meta charset="UTF-8">
    <title>智序自动化测试</title>
       <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.5.0/css/bootstrap.min.css">
    <script src=" https://cdn.staticfile.org/jquery/2.0.0/jquery.min.js"></script>
    <script src="https://cdn.staticfile.org/echarts/5.1.2/echarts.min.js"></script>
    <!--    页面样式-->
    <style type="text/css">
        /*标题样式*/

        .main {
            background: rgba(7, 10, 88, 0.99);
        }

        .title {
            width: auto;
            height: 80px;
            text-align: center;
            font: bolder 30px/80px "Microsoft YaHei UI";
            color: #fff;
            background: rgba(3, 14, 70, 0.5);
            border-bottom: solid 1px rgb(3, 14, 70);
            margin-bottom: 10px;
        }

        .content-box {
            height: 700px;
            width: 100%;
            display: flex;
            margin-bottom: 10px;
        }

        .box1 {
            flex: 3;
        }

        .box2 {
            flex: 5;
            margin: 0 10px;
        }

        .box3 {
            flex: 4;
        }

        .panel {
            position: relative;
            border: 1px solid rgba(25, 186, 139, 0.17);
            background: rgba(3, 14, 70, 0.5);
            padding: 0 10px 50px;
            margin-bottom: 10px;
        }

        .panel h2, .test_info h2 {
            height: 40px;
            line-height: 40px;
            text-align: center;
            color: #007bff;
            font-size: 20px;
            font-weight: bold;
            margin: 0;
        }

        .panel .chart {
            height: 250px;
        }

        .panel .chart2 {
            height: 350px;
        }

        .panel .desc {
            height: 150px;
        }

        .panel .desc .info {
            font: normal 18px/25px "Microsoft YaHei UI";
            color: #fff;
            display: flex;
        }

        .panel .desc .info div {
            padding: 10px;
            border: solid 1px #6c757d;
        }

        .panel .desc .info div span {
            margin-left: 20px;
        }


        /* 执行信息样式   */
        .test_info {
            width: 100%;
            padding: 20px 100px;
            background: rgba(3, 14, 70, 0.5);

        }

        .table td, .table th {
            border: solid 1px #5765a4 !important;
            padding: 0 !important;
            line-height: 40px;
            text-align: center;
            height: 40px;
            color: #fff;
        }

        select {
            border: 0;
            padding: 0;
            margin: 0;
            height: 2em;
            width: 8em;
            margin-left: 2em;
        }

        option {
            text-align: center;
            height: 36px;
            font: none 18px/36px "Microsoft YaHei UI";
            color: #28a745 !important;
        }

        .test_log {
            background: rgba(163, 171, 189, 0.15);
            width: 100%;
            height: 50px;
            border-top: none;
            border-bottom: none;
            display: none;
            text-align: left;
        }

        .test_log td {
            text-align: left;
            height: 30px;
            margin: 0;
            padding-left: 3em;
            padding-right: 3em;
            font: none 18px/24px "Microsoft YaHei UI";
        }

        pre {
            margin: 0;
            white-space: pre-wrap;
            white-space: -moz-pre-wrap;
            white-space: -o-pre-wrap;
            word-wrap: break-word;
            color: #fff;
            line-height: 22px;
            font-size: 14px
        }

        .chart4 {
            overflow: auto;
            width: 100%;
            height: 600px;

        }

        .chart4::-webkit-scrollbar {
            width: 10px;

        }

        .chart4::-webkit-scrollbar-thumb {
            /*滚动条里面小方块*/
            border-radius: 5px;
            -webkit-box-shadow: inset 0 0 5px rgba(4, 0, 225, 0.62);
            background: #272789;
            height: 10px;

        }

        .chart4::-webkit-scrollbar-track {
            /*滚动条里面轨道*/
            -webkit-box-shadow: inset 0 0 5px rgba(0, 21, 255, 0.54);
            border-radius: 10px;
            background: rgba(214, 214, 214, 0.64);

        }

        /*详细内容描述的小标题*/
        .table_title {
            text-align: center;
            background: rgba(1, 2, 37, 0.72);
            font: bold 18px/30px 'Microsoft YaHei UI';
            color: #fff;
            border-radius: 10px
        }

    </style>

</head>
<body>
<div class="main">
    <div class="title">
        智序自动化测试
    </div>
    <div class="content-box">
        <div class="box1">
            <div class="panel">
                <h2>执行结果</h2>
                <div class="chart" id="char3"></div>
                <div class="panel-footer"></div>
            </div>
            <div class="panel ">
                <h2>成功占比</h2>
                <div class="chart" id="char1"></div>
                <div class="panel-footer"></div>
            </div>
        </div>
        <div class="box2">
            <div class="panel">
                <h2>运行信息</h2>
                <div class="desc">

                    <div class="info">
                        <div style="flex:5">
                            <button type="button" class="btn btn-primary btn-sm">开始时间</button>
                            <span>2023-01-14 23:17:29</span>
                        </div>
                        <div style="flex:5">
                            <button type="button" class="btn btn-primary btn-sm">用例总数</button>
                            <span>7</span>
                        </div>
                    </div>
                    <div class="info">
                        <div style="flex:5">
                            <button type="button" class="btn btn-primary btn-sm">运行时长</button>
                            <span>23.791720 S</span>
                        </div>
                        <div style="flex:5">
                            <button type="button" class="btn btn-primary btn-sm ">测试人员</button>
                            <span>王娟</span>
                        </div>
                    </div>
                    <div class="info">
                        <div style="flex:5">
                            <button type="button" class="btn btn-primary btn-sm">成功用例</button>
                            <span>5</span>
                        </div>
                        <div style="flex:5">
                            <button type="button" class="btn btn-primary btn-sm ">通过率</button>
                            <span>71.43%</span>
                        </div>
                    </div>
                </div>
            </div>
            <div class="panel">
                <h2>通过率趋势图</h2>
                <div class="chart2" id="char2"></div>
            </div>
        </div>
        <div class="box3">
            <div class="panel">
                <h2>历史构建结果</h2>
                <div class="chart4">
                    <table class="table" style="color: #d6d6d6;padding: 0">
                        <thead>
                        <tr>
                            <th scope="col">执行时间</th>
                            <th scope="col">用例总数</th>
                            <th scope="col">成功用例数</th>
                            <th scope="col">通过率</th>
                        </tr>
                        </thead>
                        <tbody>
                        
                        <tr>
                            <th scope="row">2023-01-14 23:17:29</th>
                            <td>7</td>
                            <td>5</td>
                            <td>71.43%</td>
                        </tr>
                        
                        <tr>
                            <th scope="row">2023-01-14 23:16:44</th>
                            <td>7</td>
                            <td>5</td>
                            <td>71.43%</td>
                        </tr>
                        
                        <tr>
                            <th scope="row">2023-01-14 22:52:12</th>
                            <td>7</td>
                            <td>5</td>
                            <td>71.43%</td>
                        </tr>
                        
                        <tr>
                            <th scope="row">2023-01-13 14:45:37</th>
                            <td>8</td>
                            <td>8</td>
                            <td>100.00%</td>
                        </tr>
                        
                        </tbody>
                    </table>

                </div>
            </div>

        </div>
    </div>

    <div class="test_info">
        <h2>本次运行详情</h2>
        <div class="table_data">

            <table class="table" style="color: #fff">
                <thead class="text-light" style="background: rgba(3, 14, 70, 0.5)">
                <tr>
                    <th scope="col" style="width: 5%;padding: 0">编号</th>
                    <th scope="col" style="width: 20%;padding: 0">
                        <span>用例模块</span>
                        <select id="testClass">
                            <option>所有</option>
                            
                            <option value="test_casetest_loginpy">test_case\test_login.py</option>
                            
                            <option value="test_casetest_shouzhipy">test_case\test_shouzhi.py</option>
                            
                        </select>

                    </th>
                    <th scope="col" style="width: 15%;padding: 0">测试方法</th>
                    <th scope="col" style="width: 20%;padding: 0">用例描述</th>
                    <th scope="col" style="width: 10%;padding: 0">执行时间</th>
                    <th scope="col" style="width: 20%;padding: 0">

                        <span>执行结果</span>
                        <select id="testResult">
                            <option>所有</option>
                            <option class="text-success">成功</option>
                            <option class="text-danger">失败</option>
                            <option class="text-warning">错误</option>
                            <option class="text-info">跳过</option>
                        </select>

                    </th>
                    <th scope="col" style="width: 10%;padding: 0">详细信息</th>
                </tr>
                </thead>
                <tbody>
                
                <tr class="case_">
                    <td>1</td>
                    <td class="test_casetest_loginpy">test_case\test_login.py</td>
                    <td>TestLogin.test_login</td>
                    <td>获取新闻列表</td>
                    <td>0.215104</td>
                    
                    
                    <td class="text-success">通过</td>
                    
                    
                    <td>
                        <button type="button" class="btn btn_info btn-primary btn-sm">查看详情</button>
                    </td>
                </tr>

                <tr class="test_log">
                    <td colspan="7" class="small text-muted" style=" word-wrap:break-word; word-break:break-all;padding: 0">
                        <div class="card" style="background: rgba(3, 14, 70, 0.5)">
                            <div class="card-body">
                                <ul class="nav nav-tabs" role="tablist">
                                    <li class="nav-item" role="presentation">
                                        <a class="nav-link active" data-toggle="tab" role="tab" aria-controls="home"
                                           aria-selected="true" style="padding: 0 16px">运行日志</a>
                                    </li>
                                    <li class="nav-item" role="presentation">
                                        <a class="nav-link" data-toggle="tab" role="tab" aria-controls="profile"
                                           aria-selected="false" style="padding: 0 16px">调试输出</a>
                                    </li>
                                    
                                    

                                </ul>
                                <div class="tab-content" style="height: 300px;overflow-y: auto;padding: 20px">

                                    <div class="tab-pane fade show active" role="tabpanel" aria-labelledby="profile-tab">
                                        <pre style="line-height: 18px"></pre>
                                    </div>
                                    <div class="tab-pane fade" role="tabpanel" aria-labelledby="contact-tab">
                                        <pre style="line-height: 18px"></pre>
                                    </div>
                                    
                                    


                                </div>
                            </div>
                        </div>
                    </td>
                </tr>
                
                <tr class="case_">
                    <td>2</td>
                    <td class="test_casetest_loginpy">test_case\test_login.py</td>
                    <td>TestLogin.test_login</td>
                    <td>获取新闻列表详情</td>
                    <td>0.193080</td>
                    
                    
                    <td class="text-success">通过</td>
                    
                    
                    <td>
                        <button type="button" class="btn btn_info btn-primary btn-sm">查看详情</button>
                    </td>
                </tr>

                <tr class="test_log">
                    <td colspan="7" class="small text-muted" style=" word-wrap:break-word; word-break:break-all;padding: 0">
                        <div class="card" style="background: rgba(3, 14, 70, 0.5)">
                            <div class="card-body">
                                <ul class="nav nav-tabs" role="tablist">
                                    <li class="nav-item" role="presentation">
                                        <a class="nav-link active" data-toggle="tab" role="tab" aria-controls="home"
                                           aria-selected="true" style="padding: 0 16px">运行日志</a>
                                    </li>
                                    <li class="nav-item" role="presentation">
                                        <a class="nav-link" data-toggle="tab" role="tab" aria-controls="profile"
                                           aria-selected="false" style="padding: 0 16px">调试输出</a>
                                    </li>
                                    
                                    

                                </ul>
                                <div class="tab-content" style="height: 300px;overflow-y: auto;padding: 20px">

                                    <div class="tab-pane fade show active" role="tabpanel" aria-labelledby="profile-tab">
                                        <pre style="line-height: 18px"></pre>
                                    </div>
                                    <div class="tab-pane fade" role="tabpanel" aria-labelledby="contact-tab">
                                        <pre style="line-height: 18px"></pre>
                                    </div>
                                    
                                    


                                </div>
                            </div>
                        </div>
                    </td>
                </tr>
                
                <tr class="case_">
                    <td>3</td>
                    <td class="test_casetest_loginpy">test_case\test_login.py</td>
                    <td>TestLogin.test_login</td>
                    <td>登录功能</td>
                    <td>0.178388</td>
                    
                    
                    <td class="text-success">通过</td>
                    
                    
                    <td>
                        <button type="button" class="btn btn_info btn-primary btn-sm">查看详情</button>
                    </td>
                </tr>

                <tr class="test_log">
                    <td colspan="7" class="small text-muted" style=" word-wrap:break-word; word-break:break-all;padding: 0">
                        <div class="card" style="background: rgba(3, 14, 70, 0.5)">
                            <div class="card-body">
                                <ul class="nav nav-tabs" role="tablist">
                                    <li class="nav-item" role="presentation">
                                        <a class="nav-link active" data-toggle="tab" role="tab" aria-controls="home"
                                           aria-selected="true" style="padding: 0 16px">运行日志</a>
                                    </li>
                                    <li class="nav-item" role="presentation">
                                        <a class="nav-link" data-toggle="tab" role="tab" aria-controls="profile"
                                           aria-selected="false" style="padding: 0 16px">调试输出</a>
                                    </li>
                                    
                                    

                                </ul>
                                <div class="tab-content" style="height: 300px;overflow-y: auto;padding: 20px">

                                    <div class="tab-pane fade show active" role="tabpanel" aria-labelledby="profile-tab">
                                        <pre style="line-height: 18px"></pre>
                                    </div>
                                    <div class="tab-pane fade" role="tabpanel" aria-labelledby="contact-tab">
                                        <pre style="line-height: 18px"></pre>
                                    </div>
                                    
                                    


                                </div>
                            </div>
                        </div>
                    </td>
                </tr>
                
                <tr class="case_">
                    <td>4</td>
                    <td class="test_casetest_loginpy">test_case\test_login.py</td>
                    <td>TestLogin.test_login</td>
                    <td>收藏新闻</td>
                    <td>0.204751</td>
                    
                    
                    <td class="text-success">通过</td>
                    
                    
                    <td>
                        <button type="button" class="btn btn_info btn-primary btn-sm">查看详情</button>
                    </td>
                </tr>

                <tr class="test_log">
                    <td colspan="7" class="small text-muted" style=" word-wrap:break-word; word-break:break-all;padding: 0">
                        <div class="card" style="background: rgba(3, 14, 70, 0.5)">
                            <div class="card-body">
                                <ul class="nav nav-tabs" role="tablist">
                                    <li class="nav-item" role="presentation">
                                        <a class="nav-link active" data-toggle="tab" role="tab" aria-controls="home"
                                           aria-selected="true" style="padding: 0 16px">运行日志</a>
                                    </li>
                                    <li class="nav-item" role="presentation">
                                        <a class="nav-link" data-toggle="tab" role="tab" aria-controls="profile"
                                           aria-selected="false" style="padding: 0 16px">调试输出</a>
                                    </li>
                                    
                                    

                                </ul>
                                <div class="tab-content" style="height: 300px;overflow-y: auto;padding: 20px">

                                    <div class="tab-pane fade show active" role="tabpanel" aria-labelledby="profile-tab">
                                        <pre style="line-height: 18px"></pre>
                                    </div>
                                    <div class="tab-pane fade" role="tabpanel" aria-labelledby="contact-tab">
                                        <pre style="line-height: 18px"></pre>
                                    </div>
                                    
                                    


                                </div>
                            </div>
                        </div>
                    </td>
                </tr>
                
                <tr class="case_">
                    <td>5</td>
                    <td class="test_casetest_shouzhipy">test_case\test_shouzhi.py</td>
                    <td>TestKuaifu_shouzhi.test_shouzhi</td>
                    <td>快服网前台登录接口</td>
                    <td>1.436285</td>
                    
                    
                    <td class="text-success">通过</td>
                    
                    
                    <td>
                        <button type="button" class="btn btn_info btn-primary btn-sm">查看详情</button>
                    </td>
                </tr>

                <tr class="test_log">
                    <td colspan="7" class="small text-muted" style=" word-wrap:break-word; word-break:break-all;padding: 0">
                        <div class="card" style="background: rgba(3, 14, 70, 0.5)">
                            <div class="card-body">
                                <ul class="nav nav-tabs" role="tablist">
                                    <li class="nav-item" role="presentation">
                                        <a class="nav-link active" data-toggle="tab" role="tab" aria-controls="home"
                                           aria-selected="true" style="padding: 0 16px">运行日志</a>
                                    </li>
                                    <li class="nav-item" role="presentation">
                                        <a class="nav-link" data-toggle="tab" role="tab" aria-controls="profile"
                                           aria-selected="false" style="padding: 0 16px">调试输出</a>
                                    </li>
                                    
                                    

                                </ul>
                                <div class="tab-content" style="height: 300px;overflow-y: auto;padding: 20px">

                                    <div class="tab-pane fade show active" role="tabpanel" aria-labelledby="profile-tab">
                                        <pre style="line-height: 18px"></pre>
                                    </div>
                                    <div class="tab-pane fade" role="tabpanel" aria-labelledby="contact-tab">
                                        <pre style="line-height: 18px"></pre>
                                    </div>
                                    
                                    


                                </div>
                            </div>
                        </div>
                    </td>
                </tr>
                
                <tr class="case_">
                    <td>6</td>
                    <td class="test_casetest_shouzhipy">test_case\test_shouzhi.py</td>
                    <td>TestKuaifu_shouzhi.test_shouzhi</td>
                    <td>获取全部新优惠券总数</td>
                    <td>10.371836</td>
                    
                    <td class="text-warning">失败</td>
                    
                    <td>
                        <button type="button" class="btn btn_info btn-primary btn-sm">查看详情</button>
                    </td>
                </tr>

                <tr class="test_log">
                    <td colspan="7" class="small text-muted" style=" word-wrap:break-word; word-break:break-all;padding: 0">
                        <div class="card" style="background: rgba(3, 14, 70, 0.5)">
                            <div class="card-body">
                                <ul class="nav nav-tabs" role="tablist">
                                    <li class="nav-item" role="presentation">
                                        <a class="nav-link active" data-toggle="tab" role="tab" aria-controls="home"
                                           aria-selected="true" style="padding: 0 16px">运行日志</a>
                                    </li>
                                    <li class="nav-item" role="presentation">
                                        <a class="nav-link" data-toggle="tab" role="tab" aria-controls="profile"
                                           aria-selected="false" style="padding: 0 16px">调试输出</a>
                                    </li>
                                    
                                    <li class="nav-item" role="presentation">
                                        <a class="nav-link" data-toggle="tab" role="tab" aria-controls="contact"
                                           aria-selected="false" style="padding: 0 16px">异常信息</a>
                                    </li>
                                    
                                    

                                </ul>
                                <div class="tab-content" style="height: 300px;overflow-y: auto;padding: 20px">

                                    <div class="tab-pane fade show active" role="tabpanel" aria-labelledby="profile-tab">
                                        <pre style="line-height: 18px"></pre>
                                    </div>
                                    <div class="tab-pane fade" role="tabpanel" aria-labelledby="contact-tab">
                                        <pre style="line-height: 18px"></pre>
                                    </div>
                                    
                                    <div class="tab-pane fade " role="tabpanel" aria-labelledby="home-tab">
                                        <pre style="line-height: 18px">self = <pymysql.connections.Connection object at 0x0000026481DDD630>
sock = None

    def connect(self, sock=None):
        self._closed = False
        try:
            if sock is None:
                if self.unix_socket:
                    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
                    sock.settimeout(self.connect_timeout)
                    sock.connect(self.unix_socket)
                    self.host_info = "Localhost via UNIX socket"
                    self._secure = True
                    if DEBUG:
                        print("connected using unix_socket")
                else:
                    kwargs = {}
                    if self.bind_address is not None:
                        kwargs["source_address"] = (self.bind_address, 0)
                    while True:
                        try:
>                           sock = socket.create_connection(
                                (self.host, self.port), self.connect_timeout, **kwargs
                            )

C:\Users\wj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\pymysql\connections.py:613: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

address = ('talk.71baomu.com', 3306), timeout = 10, source_address = None

    def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
                          source_address=None):
        """Connect to *address* and return the socket object.
    
        Convenience function.  Connect to *address* (a 2-tuple ``(host,
        port)``) and return the socket object.  Passing the optional
        *timeout* parameter will set the timeout on the socket instance
        before attempting to connect.  If no *timeout* is supplied, the
        global default timeout setting returned by :func:`getdefaulttimeout`
        is used.  If *source_address* is set it must be a tuple of (host, port)
        for the socket to bind as a source address before making the connection.
        A host of '' or port 0 tells the OS to use the default.
        """
    
        host, port = address
        err = None
        for res in getaddrinfo(host, port, 0, SOCK_STREAM):
            af, socktype, proto, canonname, sa = res
            sock = None
            try:
                sock = socket(af, socktype, proto)
                if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
                    sock.settimeout(timeout)
                if source_address:
                    sock.bind(source_address)
                sock.connect(sa)
                # Break explicitly a reference cycle
                err = None
                return sock
    
            except error as _:
                err = _
                if sock is not None:
                    sock.close()
    
        if err is not None:
            try:
>               raise err

C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.2544.0_x64__qbz5n2kfra8p0\lib\socket.py:845: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

address = ('talk.71baomu.com', 3306), timeout = 10, source_address = None

    def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
                          source_address=None):
        """Connect to *address* and return the socket object.
    
        Convenience function.  Connect to *address* (a 2-tuple ``(host,
        port)``) and return the socket object.  Passing the optional
        *timeout* parameter will set the timeout on the socket instance
        before attempting to connect.  If no *timeout* is supplied, the
        global default timeout setting returned by :func:`getdefaulttimeout`
        is used.  If *source_address* is set it must be a tuple of (host, port)
        for the socket to bind as a source address before making the connection.
        A host of '' or port 0 tells the OS to use the default.
        """
    
        host, port = address
        err = None
        for res in getaddrinfo(host, port, 0, SOCK_STREAM):
            af, socktype, proto, canonname, sa = res
            sock = None
            try:
                sock = socket(af, socktype, proto)
                if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
                    sock.settimeout(timeout)
                if source_address:
                    sock.bind(source_address)
>               sock.connect(sa)
E               TimeoutError: timed out

C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.2544.0_x64__qbz5n2kfra8p0\lib\socket.py:833: TimeoutError

During handling of the above exception, another exception occurred:

self = <tool.handle_assert_db.HandleAssertDb object at 0x0000026481DDC5E0>
assert_db = '{\n"expect_data":"#newcount#",\n"actual_data":"SELECT COUNT(*) from cloud_coupons_record WHERE user_id = 18442  AND is_new_coupons = 1",\n"database_name":"53cloud"\n}'

    @allure.step('数据库断言')
    def assert_db(self,assert_db):
        print('打印数据库断言{}'.format(assert_db))
        try:
            if assert_db:
               '''
               数据库断言思路：
               1、excel测试用例里新增一个字段用来存放数据库断言的
               2、数据格式是期望结果，实际结果是查询的数据库sql语句
               3、其中sql语句中的值是从response或者脚本生成的变量，我们要特殊处理给他替换掉
               4、sql替换后转成字典的格式
               5、执行sql语句，判断执行sql得出的结果与测试用例里的预期结果是否一zhi，若一致则断言成功
               '''
               # 替换sql变量
               myassert_db = self.handle_replace.replace_sql(assert_db)
               # 获取实际结果
>              actul_result = MysqlHandle(myassert_db['database_name']).get_data(sql=myassert_db['actual_data'])[0]

tool\handle_assert_db.py:29: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <tool.handle_mysql.MysqlHandle object at 0x0000026481DDDBA0>
dbname = '53cloud'

    def __init__(self, dbname):
        # 创建数据库连接
>       self.db = pymysql.connect(
            host=mysql_info['host'],
            user=mysql_info['user'],
            passwd=mysql_info['password'],
            port=mysql_info['port'],
            autocommit=True,
            database=dbname,
            charset="utf8",
            cursorclass=pymysql.cursors.DictCursor
        )

tool\handle_mysql.py:13: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pymysql.connections.Connection object at 0x0000026481DDD630>

    def __init__(
        self,
        *,
        user=None,  # The first four arguments is based on DB-API 2.0 recommendation.
        password="",
        host=None,
        database=None,
        unix_socket=None,
        port=0,
        charset="",
        sql_mode=None,
        read_default_file=None,
        conv=None,
        use_unicode=True,
        client_flag=0,
        cursorclass=Cursor,
        init_command=None,
        connect_timeout=10,
        read_default_group=None,
        autocommit=False,
        local_infile=False,
        max_allowed_packet=16 * 1024 * 1024,
        defer_connect=False,
        auth_plugin_map=None,
        read_timeout=None,
        write_timeout=None,
        bind_address=None,
        binary_prefix=False,
        program_name=None,
        server_public_key=None,
        ssl=None,
        ssl_ca=None,
        ssl_cert=None,
        ssl_disabled=None,
        ssl_key=None,
        ssl_verify_cert=None,
        ssl_verify_identity=None,
        compress=None,  # not supported
        named_pipe=None,  # not supported
        passwd=None,  # deprecated
        db=None,  # deprecated
    ):
        if db is not None and database is None:
            # We will raise warining in 2022 or later.
            # See https://github.com/PyMySQL/PyMySQL/issues/939
            # warnings.warn("'db' is deprecated, use 'database'", DeprecationWarning, 3)
            database = db
        if passwd is not None and not password:
            # We will raise warining in 2022 or later.
            # See https://github.com/PyMySQL/PyMySQL/issues/939
            # warnings.warn(
            #    "'passwd' is deprecated, use 'password'", DeprecationWarning, 3
            # )
            password = passwd
    
        if compress or named_pipe:
            raise NotImplementedError(
                "compress and named_pipe arguments are not supported"
            )
    
        self._local_infile = bool(local_infile)
        if self._local_infile:
            client_flag |= CLIENT.LOCAL_FILES
    
        if read_default_group and not read_default_file:
            if sys.platform.startswith("win"):
                read_default_file = "c:\\my.ini"
            else:
                read_default_file = "/etc/my.cnf"
    
        if read_default_file:
            if not read_default_group:
                read_default_group = "client"
    
            cfg = Parser()
            cfg.read(os.path.expanduser(read_default_file))
    
            def _config(key, arg):
                if arg:
                    return arg
                try:
                    return cfg.get(read_default_group, key)
                except Exception:
                    return arg
    
            user = _config("user", user)
            password = _config("password", password)
            host = _config("host", host)
            database = _config("database", database)
            unix_socket = _config("socket", unix_socket)
            port = int(_config("port", port))
            bind_address = _config("bind-address", bind_address)
            charset = _config("default-character-set", charset)
            if not ssl:
                ssl = {}
            if isinstance(ssl, dict):
                for key in ["ca", "capath", "cert", "key", "cipher"]:
                    value = _config("ssl-" + key, ssl.get(key))
                    if value:
                        ssl[key] = value
    
        self.ssl = False
        if not ssl_disabled:
            if ssl_ca or ssl_cert or ssl_key or ssl_verify_cert or ssl_verify_identity:
                ssl = {
                    "ca": ssl_ca,
                    "check_hostname": bool(ssl_verify_identity),
                    "verify_mode": ssl_verify_cert
                    if ssl_verify_cert is not None
                    else False,
                }
                if ssl_cert is not None:
                    ssl["cert"] = ssl_cert
                if ssl_key is not None:
                    ssl["key"] = ssl_key
            if ssl:
                if not SSL_ENABLED:
                    raise NotImplementedError("ssl module not found")
                self.ssl = True
                client_flag |= CLIENT.SSL
                self.ctx = self._create_ssl_ctx(ssl)
    
        self.host = host or "localhost"
        self.port = port or 3306
        if type(self.port) is not int:
            raise ValueError("port should be of type int")
        self.user = user or DEFAULT_USER
        self.password = password or b""
        if isinstance(self.password, str):
            self.password = self.password.encode("latin1")
        self.db = database
        self.unix_socket = unix_socket
        self.bind_address = bind_address
        if not (0 < connect_timeout <= 31536000):
            raise ValueError("connect_timeout should be >0 and <=31536000")
        self.connect_timeout = connect_timeout or None
        if read_timeout is not None and read_timeout <= 0:
            raise ValueError("read_timeout should be > 0")
        self._read_timeout = read_timeout
        if write_timeout is not None and write_timeout <= 0:
            raise ValueError("write_timeout should be > 0")
        self._write_timeout = write_timeout
    
        self.charset = charset or DEFAULT_CHARSET
        self.use_unicode = use_unicode
    
        self.encoding = charset_by_name(self.charset).encoding
    
        client_flag |= CLIENT.CAPABILITIES
        if self.db:
            client_flag |= CLIENT.CONNECT_WITH_DB
    
        self.client_flag = client_flag
    
        self.cursorclass = cursorclass
    
        self._result = None
        self._affected_rows = 0
        self.host_info = "Not connected"
    
        # specified autocommit mode. None means use server default.
        self.autocommit_mode = autocommit
    
        if conv is None:
            conv = converters.conversions
    
        # Need for MySQLdb compatibility.
        self.encoders = {k: v for (k, v) in conv.items() if type(k) is not int}
        self.decoders = {k: v for (k, v) in conv.items() if type(k) is int}
        self.sql_mode = sql_mode
        self.init_command = init_command
        self.max_allowed_packet = max_allowed_packet
        self._auth_plugin_map = auth_plugin_map or {}
        self._binary_prefix = binary_prefix
        self.server_public_key = server_public_key
    
        self._connect_attrs = {
            "_client_name": "pymysql",
            "_pid": str(os.getpid()),
            "_client_version": VERSION_STRING,
        }
    
        if program_name:
            self._connect_attrs["program_name"] = program_name
    
        if defer_connect:
            self._sock = None
        else:
>           self.connect()

C:\Users\wj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\pymysql\connections.py:353: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pymysql.connections.Connection object at 0x0000026481DDD630>
sock = None

    def connect(self, sock=None):
        self._closed = False
        try:
            if sock is None:
                if self.unix_socket:
                    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
                    sock.settimeout(self.connect_timeout)
                    sock.connect(self.unix_socket)
                    self.host_info = "Localhost via UNIX socket"
                    self._secure = True
                    if DEBUG:
                        print("connected using unix_socket")
                else:
                    kwargs = {}
                    if self.bind_address is not None:
                        kwargs["source_address"] = (self.bind_address, 0)
                    while True:
                        try:
                            sock = socket.create_connection(
                                (self.host, self.port), self.connect_timeout, **kwargs
                            )
                            break
                        except (OSError, IOError) as e:
                            if e.errno == errno.EINTR:
                                continue
                            raise
                    self.host_info = "socket %s:%d" % (self.host, self.port)
                    if DEBUG:
                        print("connected using socket")
                    sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
                    sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
                sock.settimeout(None)
    
            self._sock = sock
            self._rfile = sock.makefile("rb")
            self._next_seq_id = 0
    
            self._get_server_information()
            self._request_authentication()
    
            if self.sql_mode is not None:
                c = self.cursor()
                c.execute("SET sql_mode=%s", (self.sql_mode,))
    
            if self.init_command is not None:
                c = self.cursor()
                c.execute(self.init_command)
                c.close()
                self.commit()
    
            if self.autocommit_mode is not None:
                self.autocommit(self.autocommit_mode)
        except BaseException as e:
            self._rfile = None
            if sock is not None:
                try:
                    sock.close()
                except:  # noqa
                    pass
    
            if isinstance(e, (OSError, IOError, socket.error)):
                exc = err.OperationalError(
                    2003, "Can't connect to MySQL server on %r (%s)" % (self.host, e)
                )
                # Keep original exception and traceback to investigate error.
                exc.original_exception = e
                exc.traceback = traceback.format_exc()
                if DEBUG:
                    print(exc.traceback)
>               raise exc
E               pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on 'talk.71baomu.com' (timed out)")

C:\Users\wj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\pymysql\connections.py:664: OperationalError

During handling of the above exception, another exception occurred:

self = <test_case.test_shouzhi.TestKuaifu_shouzhi object at 0x0000026481C33FD0>
szdatas = {'ID': 2, 'assert_db': '{\n"expect_data":"#newcount#",\n"actual_data":"SELECT COUNT(*) from cloud_coupons_record WHERE...oupons = 1",\n"database_name":"53cloud"\n}', 'data': '{\n"coupon":1\n}', 'expected_data': '{"message":"success"}', ...}
class_setup_teardown = (<tool.handle_replace.HandleReplace object at 0x0000026481C7A500>, <tool.handle_requests.HandleRequests object at 0x00..._data.HandleExtract object at 0x0000026481DDC370>, <tool.handle_assert_db.HandleAssertDb object at 0x0000026481DDC5E0>)

    @pytest.mark.usefixtures('class_setup_teardown')
    @allure.title("{szdatas[title]}")
    @pytest.mark.parametrize("szdatas",shouzhidata)
    @allure.story('收支相关功能测试')
    def test_shouzhi(self,szdatas,class_setup_teardown):
        # 参数替换
        handle_replace, handle_request, handle_assert, handle_extract_data, handle_database_assert = class_setup_teardown
        # 获取请求参数
        data = handle_replace.replace_data(data=szdatas['data'])
        logger.info(f'请求替换后的参数{data}')
        # 发送请求，并处理响应数据
        response = handle_request.send_request(method=szdatas['method'], url=szdatas['url'], data=data,
                                               is_upload=szdatas['is_upload'],project_name=szdatas['project_name'])
    
        # 断言，接口返回值得断言
        handle_assert.assert_response(expected_data=szdatas['expected_data'], response=response)
        # 提取响应数据
        handle_extract_data.handle_extract(extract_data=szdatas['extract_data'], response=response)
        # 数据库断言
>       handle_database_assert.assert_db(assert_db=szdatas['assert_db'])

test_case\test_shouzhi.py:34: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <tool.handle_assert_db.HandleAssertDb object at 0x0000026481DDC5E0>
assert_db = '{\n"expect_data":"#newcount#",\n"actual_data":"SELECT COUNT(*) from cloud_coupons_record WHERE user_id = 18442  AND is_new_coupons = 1",\n"database_name":"53cloud"\n}'

    @allure.step('数据库断言')
    def assert_db(self,assert_db):
        print('打印数据库断言{}'.format(assert_db))
        try:
            if assert_db:
               '''
               数据库断言思路：
               1、excel测试用例里新增一个字段用来存放数据库断言的
               2、数据格式是期望结果，实际结果是查询的数据库sql语句
               3、其中sql语句中的值是从response或者脚本生成的变量，我们要特殊处理给他替换掉
               4、sql替换后转成字典的格式
               5、执行sql语句，判断执行sql得出的结果与测试用例里的预期结果是否一zhi，若一致则断言成功
               '''
               # 替换sql变量
               myassert_db = self.handle_replace.replace_sql(assert_db)
               # 获取实际结果
               actul_result = MysqlHandle(myassert_db['database_name']).get_data(sql=myassert_db['actual_data'])[0]
               assert myassert_db['expect_data'] == str(actul_result)
               logger.info(f"数据库断言的预期结果是：{myassert_db['expect_data']}")
               logger.info(f"数据库断言的实际结果是：{actul_result}")
               allure.attach(body=f"{myassert_db['expect_data']}",name=f"断言的预期结果：{myassert_db['expect_data']}")
               allure.attach(body=f"{actul_result}", name=f"断言的实际结果：{actul_result}")
               print('数据库断言成功')
            else:
                logger.info('测试用例，数据库断言为空，不需要数据库断言')
                allure.attach(body="测试用例，数据库断言为空，不需要数据库断言", name="测试用例，数据库断言为空，不需要数据库断言")
                print('测试用例，数据库断言为空，不需要数据库断言')
        except Exception as error:
            logger.exception('数据库断言异常，请检查数据库连接是否成功')
            logger.exception(error)
>           raise AssertionError
E           AssertionError

tool\handle_assert_db.py:43: AssertionError</pre>
                                    </div>
                                    
                                    


                                </div>
                            </div>
                        </div>
                    </td>
                </tr>
                
                <tr class="case_">
                    <td>7</td>
                    <td class="test_casetest_shouzhipy">test_case\test_shouzhi.py</td>
                    <td>TestKuaifu_shouzhi.test_shouzhi</td>
                    <td>获取全部老优惠券总数</td>
                    <td>10.308339</td>
                    
                    <td class="text-warning">失败</td>
                    
                    <td>
                        <button type="button" class="btn btn_info btn-primary btn-sm">查看详情</button>
                    </td>
                </tr>

                <tr class="test_log">
                    <td colspan="7" class="small text-muted" style=" word-wrap:break-word; word-break:break-all;padding: 0">
                        <div class="card" style="background: rgba(3, 14, 70, 0.5)">
                            <div class="card-body">
                                <ul class="nav nav-tabs" role="tablist">
                                    <li class="nav-item" role="presentation">
                                        <a class="nav-link active" data-toggle="tab" role="tab" aria-controls="home"
                                           aria-selected="true" style="padding: 0 16px">运行日志</a>
                                    </li>
                                    <li class="nav-item" role="presentation">
                                        <a class="nav-link" data-toggle="tab" role="tab" aria-controls="profile"
                                           aria-selected="false" style="padding: 0 16px">调试输出</a>
                                    </li>
                                    
                                    <li class="nav-item" role="presentation">
                                        <a class="nav-link" data-toggle="tab" role="tab" aria-controls="contact"
                                           aria-selected="false" style="padding: 0 16px">异常信息</a>
                                    </li>
                                    
                                    

                                </ul>
                                <div class="tab-content" style="height: 300px;overflow-y: auto;padding: 20px">

                                    <div class="tab-pane fade show active" role="tabpanel" aria-labelledby="profile-tab">
                                        <pre style="line-height: 18px"></pre>
                                    </div>
                                    <div class="tab-pane fade" role="tabpanel" aria-labelledby="contact-tab">
                                        <pre style="line-height: 18px"></pre>
                                    </div>
                                    
                                    <div class="tab-pane fade " role="tabpanel" aria-labelledby="home-tab">
                                        <pre style="line-height: 18px">self = <pymysql.connections.Connection object at 0x0000026481C13BE0>
sock = None

    def connect(self, sock=None):
        self._closed = False
        try:
            if sock is None:
                if self.unix_socket:
                    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
                    sock.settimeout(self.connect_timeout)
                    sock.connect(self.unix_socket)
                    self.host_info = "Localhost via UNIX socket"
                    self._secure = True
                    if DEBUG:
                        print("connected using unix_socket")
                else:
                    kwargs = {}
                    if self.bind_address is not None:
                        kwargs["source_address"] = (self.bind_address, 0)
                    while True:
                        try:
>                           sock = socket.create_connection(
                                (self.host, self.port), self.connect_timeout, **kwargs
                            )

C:\Users\wj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\pymysql\connections.py:613: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

address = ('talk.71baomu.com', 3306), timeout = 10, source_address = None

    def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
                          source_address=None):
        """Connect to *address* and return the socket object.
    
        Convenience function.  Connect to *address* (a 2-tuple ``(host,
        port)``) and return the socket object.  Passing the optional
        *timeout* parameter will set the timeout on the socket instance
        before attempting to connect.  If no *timeout* is supplied, the
        global default timeout setting returned by :func:`getdefaulttimeout`
        is used.  If *source_address* is set it must be a tuple of (host, port)
        for the socket to bind as a source address before making the connection.
        A host of '' or port 0 tells the OS to use the default.
        """
    
        host, port = address
        err = None
        for res in getaddrinfo(host, port, 0, SOCK_STREAM):
            af, socktype, proto, canonname, sa = res
            sock = None
            try:
                sock = socket(af, socktype, proto)
                if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
                    sock.settimeout(timeout)
                if source_address:
                    sock.bind(source_address)
                sock.connect(sa)
                # Break explicitly a reference cycle
                err = None
                return sock
    
            except error as _:
                err = _
                if sock is not None:
                    sock.close()
    
        if err is not None:
            try:
>               raise err

C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.2544.0_x64__qbz5n2kfra8p0\lib\socket.py:845: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

address = ('talk.71baomu.com', 3306), timeout = 10, source_address = None

    def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
                          source_address=None):
        """Connect to *address* and return the socket object.
    
        Convenience function.  Connect to *address* (a 2-tuple ``(host,
        port)``) and return the socket object.  Passing the optional
        *timeout* parameter will set the timeout on the socket instance
        before attempting to connect.  If no *timeout* is supplied, the
        global default timeout setting returned by :func:`getdefaulttimeout`
        is used.  If *source_address* is set it must be a tuple of (host, port)
        for the socket to bind as a source address before making the connection.
        A host of '' or port 0 tells the OS to use the default.
        """
    
        host, port = address
        err = None
        for res in getaddrinfo(host, port, 0, SOCK_STREAM):
            af, socktype, proto, canonname, sa = res
            sock = None
            try:
                sock = socket(af, socktype, proto)
                if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
                    sock.settimeout(timeout)
                if source_address:
                    sock.bind(source_address)
>               sock.connect(sa)
E               TimeoutError: timed out

C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.2544.0_x64__qbz5n2kfra8p0\lib\socket.py:833: TimeoutError

During handling of the above exception, another exception occurred:

self = <tool.handle_assert_db.HandleAssertDb object at 0x0000026481DDC5E0>
assert_db = '{\n"expect_data":"#oldcount#",\n"actual_data":"SELECT COUNT(*) from cloud_coupons_record WHERE user_id = 18442 AND is_new_coupons = 2",\n"database_name":"53cloud"\n}'

    @allure.step('数据库断言')
    def assert_db(self,assert_db):
        print('打印数据库断言{}'.format(assert_db))
        try:
            if assert_db:
               '''
               数据库断言思路：
               1、excel测试用例里新增一个字段用来存放数据库断言的
               2、数据格式是期望结果，实际结果是查询的数据库sql语句
               3、其中sql语句中的值是从response或者脚本生成的变量，我们要特殊处理给他替换掉
               4、sql替换后转成字典的格式
               5、执行sql语句，判断执行sql得出的结果与测试用例里的预期结果是否一zhi，若一致则断言成功
               '''
               # 替换sql变量
               myassert_db = self.handle_replace.replace_sql(assert_db)
               # 获取实际结果
>              actul_result = MysqlHandle(myassert_db['database_name']).get_data(sql=myassert_db['actual_data'])[0]

tool\handle_assert_db.py:29: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <tool.handle_mysql.MysqlHandle object at 0x0000026481C13B50>
dbname = '53cloud'

    def __init__(self, dbname):
        # 创建数据库连接
>       self.db = pymysql.connect(
            host=mysql_info['host'],
            user=mysql_info['user'],
            passwd=mysql_info['password'],
            port=mysql_info['port'],
            autocommit=True,
            database=dbname,
            charset="utf8",
            cursorclass=pymysql.cursors.DictCursor
        )

tool\handle_mysql.py:13: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pymysql.connections.Connection object at 0x0000026481C13BE0>

    def __init__(
        self,
        *,
        user=None,  # The first four arguments is based on DB-API 2.0 recommendation.
        password="",
        host=None,
        database=None,
        unix_socket=None,
        port=0,
        charset="",
        sql_mode=None,
        read_default_file=None,
        conv=None,
        use_unicode=True,
        client_flag=0,
        cursorclass=Cursor,
        init_command=None,
        connect_timeout=10,
        read_default_group=None,
        autocommit=False,
        local_infile=False,
        max_allowed_packet=16 * 1024 * 1024,
        defer_connect=False,
        auth_plugin_map=None,
        read_timeout=None,
        write_timeout=None,
        bind_address=None,
        binary_prefix=False,
        program_name=None,
        server_public_key=None,
        ssl=None,
        ssl_ca=None,
        ssl_cert=None,
        ssl_disabled=None,
        ssl_key=None,
        ssl_verify_cert=None,
        ssl_verify_identity=None,
        compress=None,  # not supported
        named_pipe=None,  # not supported
        passwd=None,  # deprecated
        db=None,  # deprecated
    ):
        if db is not None and database is None:
            # We will raise warining in 2022 or later.
            # See https://github.com/PyMySQL/PyMySQL/issues/939
            # warnings.warn("'db' is deprecated, use 'database'", DeprecationWarning, 3)
            database = db
        if passwd is not None and not password:
            # We will raise warining in 2022 or later.
            # See https://github.com/PyMySQL/PyMySQL/issues/939
            # warnings.warn(
            #    "'passwd' is deprecated, use 'password'", DeprecationWarning, 3
            # )
            password = passwd
    
        if compress or named_pipe:
            raise NotImplementedError(
                "compress and named_pipe arguments are not supported"
            )
    
        self._local_infile = bool(local_infile)
        if self._local_infile:
            client_flag |= CLIENT.LOCAL_FILES
    
        if read_default_group and not read_default_file:
            if sys.platform.startswith("win"):
                read_default_file = "c:\\my.ini"
            else:
                read_default_file = "/etc/my.cnf"
    
        if read_default_file:
            if not read_default_group:
                read_default_group = "client"
    
            cfg = Parser()
            cfg.read(os.path.expanduser(read_default_file))
    
            def _config(key, arg):
                if arg:
                    return arg
                try:
                    return cfg.get(read_default_group, key)
                except Exception:
                    return arg
    
            user = _config("user", user)
            password = _config("password", password)
            host = _config("host", host)
            database = _config("database", database)
            unix_socket = _config("socket", unix_socket)
            port = int(_config("port", port))
            bind_address = _config("bind-address", bind_address)
            charset = _config("default-character-set", charset)
            if not ssl:
                ssl = {}
            if isinstance(ssl, dict):
                for key in ["ca", "capath", "cert", "key", "cipher"]:
                    value = _config("ssl-" + key, ssl.get(key))
                    if value:
                        ssl[key] = value
    
        self.ssl = False
        if not ssl_disabled:
            if ssl_ca or ssl_cert or ssl_key or ssl_verify_cert or ssl_verify_identity:
                ssl = {
                    "ca": ssl_ca,
                    "check_hostname": bool(ssl_verify_identity),
                    "verify_mode": ssl_verify_cert
                    if ssl_verify_cert is not None
                    else False,
                }
                if ssl_cert is not None:
                    ssl["cert"] = ssl_cert
                if ssl_key is not None:
                    ssl["key"] = ssl_key
            if ssl:
                if not SSL_ENABLED:
                    raise NotImplementedError("ssl module not found")
                self.ssl = True
                client_flag |= CLIENT.SSL
                self.ctx = self._create_ssl_ctx(ssl)
    
        self.host = host or "localhost"
        self.port = port or 3306
        if type(self.port) is not int:
            raise ValueError("port should be of type int")
        self.user = user or DEFAULT_USER
        self.password = password or b""
        if isinstance(self.password, str):
            self.password = self.password.encode("latin1")
        self.db = database
        self.unix_socket = unix_socket
        self.bind_address = bind_address
        if not (0 < connect_timeout <= 31536000):
            raise ValueError("connect_timeout should be >0 and <=31536000")
        self.connect_timeout = connect_timeout or None
        if read_timeout is not None and read_timeout <= 0:
            raise ValueError("read_timeout should be > 0")
        self._read_timeout = read_timeout
        if write_timeout is not None and write_timeout <= 0:
            raise ValueError("write_timeout should be > 0")
        self._write_timeout = write_timeout
    
        self.charset = charset or DEFAULT_CHARSET
        self.use_unicode = use_unicode
    
        self.encoding = charset_by_name(self.charset).encoding
    
        client_flag |= CLIENT.CAPABILITIES
        if self.db:
            client_flag |= CLIENT.CONNECT_WITH_DB
    
        self.client_flag = client_flag
    
        self.cursorclass = cursorclass
    
        self._result = None
        self._affected_rows = 0
        self.host_info = "Not connected"
    
        # specified autocommit mode. None means use server default.
        self.autocommit_mode = autocommit
    
        if conv is None:
            conv = converters.conversions
    
        # Need for MySQLdb compatibility.
        self.encoders = {k: v for (k, v) in conv.items() if type(k) is not int}
        self.decoders = {k: v for (k, v) in conv.items() if type(k) is int}
        self.sql_mode = sql_mode
        self.init_command = init_command
        self.max_allowed_packet = max_allowed_packet
        self._auth_plugin_map = auth_plugin_map or {}
        self._binary_prefix = binary_prefix
        self.server_public_key = server_public_key
    
        self._connect_attrs = {
            "_client_name": "pymysql",
            "_pid": str(os.getpid()),
            "_client_version": VERSION_STRING,
        }
    
        if program_name:
            self._connect_attrs["program_name"] = program_name
    
        if defer_connect:
            self._sock = None
        else:
>           self.connect()

C:\Users\wj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\pymysql\connections.py:353: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pymysql.connections.Connection object at 0x0000026481C13BE0>
sock = None

    def connect(self, sock=None):
        self._closed = False
        try:
            if sock is None:
                if self.unix_socket:
                    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
                    sock.settimeout(self.connect_timeout)
                    sock.connect(self.unix_socket)
                    self.host_info = "Localhost via UNIX socket"
                    self._secure = True
                    if DEBUG:
                        print("connected using unix_socket")
                else:
                    kwargs = {}
                    if self.bind_address is not None:
                        kwargs["source_address"] = (self.bind_address, 0)
                    while True:
                        try:
                            sock = socket.create_connection(
                                (self.host, self.port), self.connect_timeout, **kwargs
                            )
                            break
                        except (OSError, IOError) as e:
                            if e.errno == errno.EINTR:
                                continue
                            raise
                    self.host_info = "socket %s:%d" % (self.host, self.port)
                    if DEBUG:
                        print("connected using socket")
                    sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
                    sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
                sock.settimeout(None)
    
            self._sock = sock
            self._rfile = sock.makefile("rb")
            self._next_seq_id = 0
    
            self._get_server_information()
            self._request_authentication()
    
            if self.sql_mode is not None:
                c = self.cursor()
                c.execute("SET sql_mode=%s", (self.sql_mode,))
    
            if self.init_command is not None:
                c = self.cursor()
                c.execute(self.init_command)
                c.close()
                self.commit()
    
            if self.autocommit_mode is not None:
                self.autocommit(self.autocommit_mode)
        except BaseException as e:
            self._rfile = None
            if sock is not None:
                try:
                    sock.close()
                except:  # noqa
                    pass
    
            if isinstance(e, (OSError, IOError, socket.error)):
                exc = err.OperationalError(
                    2003, "Can't connect to MySQL server on %r (%s)" % (self.host, e)
                )
                # Keep original exception and traceback to investigate error.
                exc.original_exception = e
                exc.traceback = traceback.format_exc()
                if DEBUG:
                    print(exc.traceback)
>               raise exc
E               pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on 'talk.71baomu.com' (timed out)")

C:\Users\wj\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\pymysql\connections.py:664: OperationalError

During handling of the above exception, another exception occurred:

self = <test_case.test_shouzhi.TestKuaifu_shouzhi object at 0x0000026481C336D0>
szdatas = {'ID': 3, 'assert_db': '{\n"expect_data":"#oldcount#",\n"actual_data":"SELECT COUNT(*) from cloud_coupons_record WHERE...oupons = 2",\n"database_name":"53cloud"\n}', 'data': '{\n"coupon":2\n}', 'expected_data': '{"message":"success"}', ...}
class_setup_teardown = (<tool.handle_replace.HandleReplace object at 0x0000026481C7A500>, <tool.handle_requests.HandleRequests object at 0x00..._data.HandleExtract object at 0x0000026481DDC370>, <tool.handle_assert_db.HandleAssertDb object at 0x0000026481DDC5E0>)

    @pytest.mark.usefixtures('class_setup_teardown')
    @allure.title("{szdatas[title]}")
    @pytest.mark.parametrize("szdatas",shouzhidata)
    @allure.story('收支相关功能测试')
    def test_shouzhi(self,szdatas,class_setup_teardown):
        # 参数替换
        handle_replace, handle_request, handle_assert, handle_extract_data, handle_database_assert = class_setup_teardown
        # 获取请求参数
        data = handle_replace.replace_data(data=szdatas['data'])
        logger.info(f'请求替换后的参数{data}')
        # 发送请求，并处理响应数据
        response = handle_request.send_request(method=szdatas['method'], url=szdatas['url'], data=data,
                                               is_upload=szdatas['is_upload'],project_name=szdatas['project_name'])
    
        # 断言，接口返回值得断言
        handle_assert.assert_response(expected_data=szdatas['expected_data'], response=response)
        # 提取响应数据
        handle_extract_data.handle_extract(extract_data=szdatas['extract_data'], response=response)
        # 数据库断言
>       handle_database_assert.assert_db(assert_db=szdatas['assert_db'])

test_case\test_shouzhi.py:34: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <tool.handle_assert_db.HandleAssertDb object at 0x0000026481DDC5E0>
assert_db = '{\n"expect_data":"#oldcount#",\n"actual_data":"SELECT COUNT(*) from cloud_coupons_record WHERE user_id = 18442 AND is_new_coupons = 2",\n"database_name":"53cloud"\n}'

    @allure.step('数据库断言')
    def assert_db(self,assert_db):
        print('打印数据库断言{}'.format(assert_db))
        try:
            if assert_db:
               '''
               数据库断言思路：
               1、excel测试用例里新增一个字段用来存放数据库断言的
               2、数据格式是期望结果，实际结果是查询的数据库sql语句
               3、其中sql语句中的值是从response或者脚本生成的变量，我们要特殊处理给他替换掉
               4、sql替换后转成字典的格式
               5、执行sql语句，判断执行sql得出的结果与测试用例里的预期结果是否一zhi，若一致则断言成功
               '''
               # 替换sql变量
               myassert_db = self.handle_replace.replace_sql(assert_db)
               # 获取实际结果
               actul_result = MysqlHandle(myassert_db['database_name']).get_data(sql=myassert_db['actual_data'])[0]
               assert myassert_db['expect_data'] == str(actul_result)
               logger.info(f"数据库断言的预期结果是：{myassert_db['expect_data']}")
               logger.info(f"数据库断言的实际结果是：{actul_result}")
               allure.attach(body=f"{myassert_db['expect_data']}",name=f"断言的预期结果：{myassert_db['expect_data']}")
               allure.attach(body=f"{actul_result}", name=f"断言的实际结果：{actul_result}")
               print('数据库断言成功')
            else:
                logger.info('测试用例，数据库断言为空，不需要数据库断言')
                allure.attach(body="测试用例，数据库断言为空，不需要数据库断言", name="测试用例，数据库断言为空，不需要数据库断言")
                print('测试用例，数据库断言为空，不需要数据库断言')
        except Exception as error:
            logger.exception('数据库断言异常，请检查数据库连接是否成功')
            logger.exception(error)
>           raise AssertionError
E           AssertionError

tool\handle_assert_db.py:43: AssertionError</pre>
                                    </div>
                                    
                                    


                                </div>
                            </div>
                        </div>
                    </td>
                </tr>
                
            </table>
        </div>

        <div style="height: 200px"></div>
    </div>

</div>




<script type="text/javascript">

    const resulte = {
            "success": `5`,
            "all": `7`,
            "fail": `2`,
            "skip": '0',
            "error": `0`,
            "runtime": '23.791720 S',
            "begin_time": "1673709449.230555",
            "pass_rate": '71.43',
        }
    ;
    const history = [{'success': 8, 'all': 8, 'fail': 0, 'skip': 0, 'error': 0, 'runtime': '9.215318 S', 'begin_time': '2023-01-13 14:45:37', 'pass_rate': '100.00'}, {'success': 5, 'all': 7, 'fail': 2, 'skip': 0, 'error': 0, 'runtime': '29.444870 S', 'begin_time': '2023-01-14 22:52:12', 'pass_rate': '71.43'}, {'success': 5, 'all': 7, 'fail': 2, 'skip': 0, 'error': 0, 'runtime': '23.959758 S', 'begin_time': '2023-01-14 23:16:44', 'pass_rate': '71.43'}, {'success': 5, 'all': 7, 'fail': 2, 'skip': 0, 'error': 0, 'runtime': '23.791720 S', 'begin_time': '2023-01-14 23:17:29', 'pass_rate': '71.43'}];

    var passRate = [];
    var dTime = [];
    history.forEach(function (item, index, array) {
        passRate.push(item.pass_rate);
        dTime.push(item.begin_time)
    });
    if (passRate.length === 1) {
        passRate.unshift(0);
        dTime.unshift(0)
    }

    function char01() {
        let myChart = echarts.init(document.getElementById('char1'));
        let option = {
            color: ['#28a745', '#ffc107', '#dc3545', '#17a2b8'],
            tooltip: {
                trigger: 'item',
                formatter: '{a} <br/>{b}: {c} ({d}%)',
                backgroundColor: 'rgba(3, 14, 70, 0.5)',
                borderColor: '#333',
                textStyle: {
                    color: '#fff',
                    fontSize: "13"
                },
            },
            legend: {
                bottom: "0%",
                // 小图标的宽度和高度
                itemWidth: 10,
                itemHeight: 10,
                data: ['通过', '失败', '错误', '跳过'],
                textStyle: {
                    color: "rgba(255,255,255,.5)",
                    fontSize: "12"
                },
            },
            series: [
                {
                    name: '测试结果',
                    type: 'pie',
                    radius: ['50%', '70%'],
                    avoidLabelOverlap: false,
                    label: {
                        show: false,
                        position: 'center'
                    },
                    emphasis: {
                        label: {
                            show: true,
                            fontSize: '20',
                            fontWeight: 'bold',
                            color: '#fff',
                        }
                    },
                    labelLine: {
                        show: false
                    },
                    data: [
                        {value: resulte.success, name: '通过'},
                        {value: resulte.fail, name: '失败'},
                        {value: resulte.error, name: '错误'},
                        {value: resulte.skip, name: '跳过'}
                    ]
                }
            ]
        };
        myChart.setOption(option)

    }

    char01();

    //    大图
    function char02() {

        // 基于准备好的dom，初始化echarts实例
        let myChart = echarts.init(document.getElementById("char2"));
        // 2. 指定配置和数据
        option = {
            tooltip: {
                trigger: 'axis',
                formatter: '{a} <br/>{b}:  ({c}%)',
                backgroundColor: 'rgba(3, 14, 70, 0.5)',
                borderColor: '#333',
                textStyle: {
                    color: '#fff',
                    fontSize: "13"
                },
            },

            grid: {
                left: "10",
                top: "30",
                right: "10",
                bottom: "0",
                containLabel: true
            },

            xAxis: [{
                type: "category",
                boundaryGap: false,
                show: false,
                axisLabel: {
                    textStyle: {
                        color: "rgba(255,255,255,.6)",
                        fontSize: 12
                    }
                },
                axisLine: {
                    lineStyle: {
                        color: "rgba(255,255,255,.2)"
                    }
                },

                data: dTime
            },
                {
                    axisPointer: {
                        show: false
                    },
                    axisLine: {
                        show: false
                    },
                    position: "bottom",
                    offset: 20
                }
            ],

            yAxis: [{
                type: "value",
                axisTick: {
                    show: false
                },
                axisLine: {
                    lineStyle: {
                        color: "rgba(255,255,255,.1)"
                    }
                },
                axisLabel: {
                    textStyle: {
                        color: "rgba(255,255,255,.6)",
                        fontSize: 12
                    }
                },

                splitLine: {
                    lineStyle: {
                        color: "rgba(255,255,255,.1)"
                    }
                }
            }],
            series: [{
                name: "通过率",
                type: "line",
                smooth: true,
                symbol: "circle",
                symbolSize: 5,
                showSymbol: true,
                lineStyle: {
                    normal: {
                        color: "#0184d5",
                        width: 2
                    }
                },
                areaStyle: {
                    normal: {
                        color: new echarts.graphic.LinearGradient(
                            0,
                            0,
                            0,
                            1,
                            [{
                                offset: 0,
                                color: "rgba(1, 132, 213, 0.4)"
                            },
                                {
                                    offset: 0.8,
                                    color: "rgba(1, 132, 213, 0.1)"
                                }
                            ],
                            false
                        ),
                        shadowColor: "rgba(0, 0, 0, 0.1)"
                    }
                },
                itemStyle: {
                    normal: {
                        color: "#0184d5",
                        borderColor: "rgba(221, 220, 107, .1)",
                        borderWidth: 18
                    }
                },
                data: passRate
            },
            ]
        };
        // 重新把配置好的新数据给实例对象
        myChart.setOption(option);

    }

    char02();

    function char03() {
        // 基于准备好的dom，初始化echarts实例
        let myChart = echarts.init(document.getElementById("char3"));
        var data = [resulte.success, resulte.fail, resulte.error, resulte.skip];
        var titlename = ["通过用例", "失败用例", "错误用例", "跳过用例",];
        var valdata = [resulte.all, resulte.all, resulte.all, resulte.all];
        var myColor = ['#28a745', '#ffc107', '#dc3545', '#17a2b8'];
        option = {
            //图标位置
            grid: {
                top: "10%",
                left: "22%",
                bottom: "10%"
            },
            xAxis: {
                show: false
            },
            yAxis: [{
                show: true,
                data: titlename,
                inverse: true,
                axisLine: {
                    show: false
                },
                splitLine: {
                    show: false
                },
                axisTick: {
                    show: false
                },
                axisLabel: {
                    color: "#fff",

                    rich: {
                        lg: {
                            backgroundColor: "#339911",
                            color: "#fff",
                            borderRadius: 15,
                            align: "center",
                            width: 15,
                            height: 15
                        }
                    }
                }
            },
                {
                    show: false,
                    inverse: true,
                    data: valdata,
                    axisLabel: {
                        textStyle: {
                            fontSize: 12,
                            color: "#fff"
                        }
                    },
                    axisTick: {
                        show: false
                    },
                    axisLine: {
                        show: false
                    },
                }
            ],
            series: [{
                type: "bar",
                yAxisIndex: 0,
                data: data,
                barCategoryGap: 50,
                barWidth: 18,
                itemStyle: {
                    normal: {
                        barBorderRadius: 20,
                        color: function (params) {
                            var num = myColor.length;
                            return myColor[params.dataIndex % num];
                        }
                    }
                },
                label: {
                    normal: {
                        show: true,
                        position: "right",
                        formatter: "{c}条",
                        color: "#fff",

                    }
                }
            },
                {
                    type: "bar",
                    yAxisIndex: 1,
                    barCategoryGap: 50,
                    data: valdata,
                    barWidth: 20,
                    itemStyle: {
                        normal: {
                            color: "none",
                            borderColor: "#00c1de",
                            borderWidth: 2,
                            barBorderRadius: 15
                        }
                    }
                }
            ]
        };

        // 使用刚指定的配置项和数据显示图表。
        myChart.setOption(option);
        window.addEventListener("resize", function () {
            myChart.resize();
        });
    }

    char03()

</script>


<script>
    var tbodyTr = $('tbody .case_');
    var testResult = $("#testResult");
    var testClass = $("#testClass");
    <!--    用例执行详细信息显示切换-->
    $(".btn_info").click(function () {
        $(this).parent().parent().next().toggle();

    });
    // 当选择用例类之后触发
    testClass.change(function () {
        $('.test_log').hide();
        var cls = $(this).val();
        var res = testResult.val();
        elementDisplay(cls, res);
        sort()
    });
    testResult.change(function () {
        var res = $(this).val();
        var cls = testClass.val();
        elementDisplay(cls, res);
        sort()
    });

    function elementDisplay(cls, res) {
        // 用例数据的显示
        if (cls === "所有") {
            if (res === "所有") {
                tbodyTr.has('button').show();
            } else if (res === '成功') {
                tbodyTr.hide();
                tbodyTr.has('button').has('.text-success').show()

            } else if (res === '错误') {
                tbodyTr.hide();
                tbodyTr.has('button').has('.text-danger').show()

            } else if (res === '失败') {
                tbodyTr.hide();
                tbodyTr.has('button').has('.text-warning').show()

            } else if (res === '跳过') {
                tbodyTr.hide();
                tbodyTr.has('button').has('.text-info').show()
            }
        } else {
            if (res === "所有") {
                tbodyTr.hide();
                tbodyTr.has('button').has('.' + cls + '').show()
            } else if (res === '成功') {
                tbodyTr.hide();
                tbodyTr.has('button').has('.' + cls + '').has('.text-success').show()
            } else if (res === '错误') {
                tbodyTr.hide();
                tbodyTr.has('button').has('.' + cls + '').has('.text-danger').show()
            } else if (res === '失败') {
                tbodyTr.hide();
                tbodyTr.has('button').has('.' + cls + '').has('.text-warning').show()
            } else if (res === '跳过') {
                tbodyTr.hide();
                tbodyTr.has('button').has('.' + cls + '').has('.text-info').show()
            }
        }
    }

    function sort() {
        //重新排列显示序号
        // 选择所有可以见的tr
        var visibleTr = tbodyTr.filter(":visible");

        visibleTr.each(function (index, element) {
            element.firstElementChild.innerHTML = index + 1;

        })

    }

    $('.nav-tabs li').click(function () {
        $(this).find('a').addClass('active');
        $(this).siblings().find('a').removeClass('active');
        $(this).parent().next().children('.tab-content div').eq($(this).index()).addClass('active show').siblings().removeClass('active show')
    });

</script>


</body>
</html>